﻿using Microsoft.Win32;
using System;
using System.Collections;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Data;
using System.Diagnostics;
using System.Globalization; // DateTime.TryParseExactに使用
using System.IO;
using System.Linq;
using System.Management; //VSで参照追加必要
using System.Net.NetworkInformation;
using System.Runtime.InteropServices;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using System.Xml;
using System.Xml.Linq;




/*
ObservePC バージョン履歴

2025.10.16 Ver1.0
- Windows 10/11 + .NET Framework4.7.2 対応のコンソールツール
- WMIを用いてPC環境情報を取得
- AIとの対話時に前提整形として活用
- GUI不要、コピーペースト最適化
- 改変・配布自由（不正改変は禁止）

2025.10.17 Ver1.1
- user_note.txt の連結機能を追加
- 起動オプションによる属人属性の明示（-L, -H, -M）
- モニター情報取得機能を追加

2025.10.21 Ver1.2
- 以下の機能を追加：
  ・スタートアップ一覧
  ・Windows Update履歴
  ・NIC構成
  ・常駐プロセス一覧

2025.10.22 Ver1.3
- 以下の修正
常駐プロセス一覧がアルファベット順に出てるだけ。これでは意味をなさない
メソッドを書き換えて、サードパーティ製プロセス一覧（常駐候補）へ切り替え

2025.10.24 Ver1.4
- 以下の修正
ターゲットフレームワーク4.7.2から4.8へ変更
取得情報の強化。BIOS情報、NIC情報拡張、OSインストール日、セキュリティ状況(セキュリティソフトの推測)、タスクスケジューラ登録
管理・教材用途を考慮してLAN出力（SMB/HTTP/Syslog/SMTP）のコメントアウト＋テンプレート追加

2025.11.7 Ver1.6
Ver1.5での失敗を踏まえて、互換性の確保に注力 PowerShell依存を完全撤廃
ターゲットフレームワーク4.7.2へ再び変更。Server2019で動作確認
サービスの出力を追加。JSONはLAN出力同様にコメントアウト
サービス一覧（異常・停止・無効)を追加

2025.11.7 Ver1.65
ファイアウォール通過許可ルール（残骸検出）をユーザー/アプリが設定したファイアウォールルールへ変更

2025.11.12 Ver1.66
タスクスケジューラーとサービス情報を改修

 2025.11.7 Ver1.67
 情報取得を並列実行へ変更 高速モード-F新設

 2025.11.16 Ver1.68
 並列処理の補完として逐次実行を追加 安全モード-S新設
 モード指定無し・-Eモード時に取得確認と補完追加

 2025.11.17 Ver1.69
 -Sモードで逐次取得を2回るように改良

2026.1.18 Ver1.75
マスターテーブル構造へ変更とメソッドの引数無しで統一
イベントログの出力件数を増加
2026.1.22 Ver1.75
NTP,電源プランの表示を追加

2026.1.25 Ver1.8
リリースバージョン。
マスタテーブルから抜けていたのを発見。修正
ウォームアップ処理の追加(コメントアウト)
Windows Update履歴の出力をインストール日でソート
 */




// Win32 API 関数と構造体の定義
class Program
{
    //グローバル(クラス内)変数の宣言
    static ConcurrentDictionary<int, string> sb_data = new ConcurrentDictionary<int, string>(); //メソッドからの値収納用
    static Dictionary<int, string> sb_Comment = new Dictionary<int, string>(); //sbのインデクス
    static Dictionary<int, string> sb_Method_Name = new Dictionary<int, string>(); //sbの取得メソッド
    static Dictionary<int, byte> WmiLevel = new Dictionary<int, byte>(); //WMIの使用量（0?3）
    static Dictionary<int, byte> LoadLevel = new Dictionary<int, byte>(); //メソッドの処理負荷（1?4）

    static byte MaxMethod = 0;   //取得メソッド(セクション)数
    static byte MaxDataOutput = 10; // 通常モード出力上限は10件
    const byte MaxDataOutput_E = 30;    //-Eモード出力上限は30件
    static bool expertMode; //-E,-F,-Sモード(拡張表示) フラグ
    static bool IsExtended;    //-E,-F,-Sモードで呼び出されるメソッドのフラグ

    const String Ver = "1.8";      //バージョン表記



    // ディスプレイ情報を取得するためのWin32 API構造体（DISPLAY_DEVICE）
    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
    public struct DISPLAY_DEVICE
    {
        public int cb; // 構造体のサイズ (Marshal.SizeOf(DISPLAY_DEVICE)で初期化)
        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)]
        public string DeviceName;
        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 128)]
        public string DeviceString; // デバイスのユーザーフレンドリーな名前
        public int StateFlags;
        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 128)]
        public string DeviceID;
        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 128)]
        public string DeviceKey;
    }

    // user32.dllからEnumDisplayDevices関数をインポート
    // これにより、Win32 APIのネイティブな機能（GPU/モニター情報取得）をC#から呼び出せる
    [DllImport("user32.dll", CharSet = CharSet.Ansi)]
    public static extern bool EnumDisplayDevices(
        string lpDevice,
        uint iDevNum,
        ref DISPLAY_DEVICE lpDisplayDevice,
        uint dwFlags
    );

    // インストール済みアプリ情報を格納するシンプルなクラス
    class InstalledApp
    {
        public string Name { get; set; }
        public DateTime? InstallDate { get; set; } // インストール日 (取得できない場合もあるためNullable)
    }


    // プログラムのエントリポイント（メイン処理）
    // 通常Mainは改変する必要は無い
    static async Task Main(string[] args)
    {
        DateTime dt1 = DateTime.Now;    //時刻取得
        Console.WriteLine(dt1);
        //WarmUp();   //軽い処理を実行してキャッシュ
        /*
         for (int i = 1; i <= 26; i++)
         {
             sb_data[i] = "";
         }
        */
        //-Eモード検出
        expertMode = args.Contains("-E", StringComparer.OrdinalIgnoreCase); 
        foreach (string arg in args)
        {
            if (arg.Equals("-E", StringComparison.OrdinalIgnoreCase))
            {
                MaxDataOutput = MaxDataOutput_E;         //-Eでの表示件数を規定。
            }
        }

        //-Fモード検出
        bool FastMode = args.Contains("-F", StringComparer.OrdinalIgnoreCase);
        foreach (string arg in args)
        {
            if (arg.Equals("-F", StringComparison.OrdinalIgnoreCase))
            {
                expertMode = true;
                MaxDataOutput = MaxDataOutput_E;
            }
        }

        //-Sモード検出
        bool SafetyMode = args.Contains("-S", StringComparer.OrdinalIgnoreCase);
        foreach (string arg in args)
        {
            if (arg.Equals("-S", StringComparison.OrdinalIgnoreCase))
            {
                expertMode = true;
                MaxDataOutput = MaxDataOutput_E;
            }
        }

        // StringBuilderは、文字列を効率よく結合するためのクラス
        var sb = new StringBuilder(8192);
        string hed = Ver;
        hed = hed + " :" + dt1 + " :" + TimeZoneInfo.Local;
        sb.Append (hed + " :");
        sb.AppendLine();
        TimeZoneInfo tz = TimeZoneInfo.Local;
        InitializeMethodMaps();

        //出力見出しをマスターテーブル(sb_Comment)から整形
        string[] sectionHeaders = sb_Comment
            .OrderBy(kv => kv.Key)
            .Select(kv => "◆ " + kv.Value + " ◆")
            .ToArray();

        /*
            string[] sectionHeaders = new[]
            {
        "?? OS情報：",
        "?? マザーボード情報：",
        "?? CPUとメモリー情報：",
        "?? ストレージ情報 (容量と空き容量)：",
        "?? 接続されているデバイス一覧（分類・最大10件）：",
        "?? GPU情報：",
        "?? ディスプレイ情報（EnumDisplayDevicesより）：",
        "?? ネットワーク構成（System.Net + WMIより）：",
        "?? 環境変数 (Path, Tempなど)：",
        "?? Windows Update履歴（KB番号とインストール日）：",
        "?? 最近インストールされたソフトウェア（最大10件）：",
        "?? スタートアップアプリ一覧：",
        "?? サードパーティ製プロセス一覧（常駐候補）：",
        "?? セキュリティソフトの常駐状況（推定）：",
        "?? ファイアウォール概要",
        "?? 異常ドライバー検出:",
        "?? 最近のシステムイベントログ（Error/Warningのみ、10016除外 最大10件）：",
        "?? ユーザーアカウント一覧：",
        "?? タスクスケジューラ登録一覧（整形済・schtasks.exeより）：",
        "?? 削除ソフト痕跡（レジストリより推定）：",
        "?? 削除ソフト痕跡（イベントログより確定）：",
        "?? サービス一覧（異常・停止・無効)：",
        "?? ユーザー/アプリが設定したファイアウォールルール:"
    };
         */

        //並列取得
        if (!SafetyMode)
        {

            if (FastMode)   //-F無制限並列取得へ
            {
                var result = await GetAllSystemDataFastAsync(expertMode);
                sb.Append(result);
            }
            else　　//オプション無し-Eモード多段限定並列取得へ
            {
                var result = await GetAllSystemDataAsync_E(expertMode);
                sb.Append(result);
            }          
        }

        //逐次取得、FastMode除く
        if (!FastMode)  //オプション無し-Eモードの2回目と-Sモードの取得
        {
            byte i = 0;
            do
            {
                GetSequential();
                i++;
                //Console.WriteLine(i);
            } while ((i < 2)&&(SafetyMode));    //SafetyModeだけ2回取得

        }

        //連結
        // すべてのsb_dataを連結（空欄はスキップ）
        //sb.AppendLine("==== 統合出力 ====");
        //int MaxIndex = 30;
        for (int i = 0; i < MaxMethod; i++) // メソッド数までループ
        {
            if (sb_data.TryGetValue(i, out string value) && !string.IsNullOrWhiteSpace(value))
            {
                string trimmed = value.TrimEnd(); // ← 末尾の改行を除去
                sb.AppendLine(trimmed);
                sb.AppendLine(); // ← 1行だけ追加
            }

        }

        //ユーザー補足情報ファイル (user_note.txt) の読み込み
        string noteFile = "user_note.txt";
        if (System.IO.File.Exists(noteFile))
        {
            sb.AppendLine("== 使用者補足情報（user_note.txt より） ==");
            // UTF8で読み込み (日本語文字化け対策)
            string[] notes = System.IO.File.ReadAllLines(noteFile, Encoding.UTF8);
            foreach (string line in notes)
            {
                sb.AppendLine(line);
            }
        }

        //起動オプションによる属人属性（AI応答プロトコルヘッダー）の追加
        List<string> userNotes = new List<string>();

        foreach (string arg in args)
        {
            // -L: Learner (初心者)
            if (arg.Equals("-L", StringComparison.OrdinalIgnoreCase))
            {
                userNotes.Add("--- [AI応答プロトコルヘッダー] ---");
                userNotes.Add("説明要求レベル: 初心者 (-L)");
                userNotes.Add("AIへの説明要求: 初心者です。専門用語を避け、平易に説明してください。");
            }
            // -H: Highly experienced (経験者)
            else if (arg.Equals("-H", StringComparison.OrdinalIgnoreCase))
            {
                userNotes.Add("--- [AI応答プロトコルヘッダー] ---");
                userNotes.Add("説明要求レベル: 経験者 (-H)");
                userNotes.Add("AIへの説明要求: 経験者です。簡潔に、要点だけを伝えてください。");
            }
            // -M=message: カスタムメッセージ
            else if (arg.StartsWith("-M=", StringComparison.OrdinalIgnoreCase) && arg.Length > 3)
            {
                string rawMessage = arg.Substring(3);
                // 引用符(' " ')やスペースをトリムしてメッセージを整形
                string message = rawMessage.Trim('"', '\'', ' ');

                if (!string.IsNullOrEmpty(message))
                {
                    userNotes.Add("--- [AI応答プロトコルヘッダー] ---");
                    userNotes.Add("説明要求レベル: 標準 (-M) + カスタム");
                    userNotes.Add($"AIへの説明要求: {message}");
                }
            }
        }

        if (userNotes.Count > 0)
        {
            sb.AppendLine();
            sb.AppendLine("== 起動オプションによる使用者属性 ==");
            foreach (string line in userNotes)
            {
                sb.AppendLine(line);
            }
        }


        // == 相談開始宣言 ==
        sb.AppendLine("以上の環境で相談があります");

        string machine = Environment.MachineName;
        string date = DateTime.Now.ToString("yyyyMMdd");

        string finalOutput = Regex.Replace(sb.ToString(), @"\x00|\x01|\x02|\x03|\x04|\x05|\x06|\x07|\x08|\x0B|\x0C|\x0E-\x1F", "");

        string txtFile = $"PC_Observation_{machine}_{date}.txt";
        string jsonFile = $"PC_Observation_{machine}_{date}.json";

        var snapshot = new Dictionary<string, string>();
        string sbText = finalOutput; // sb.ToString() 後の整形済みテキスト

        foreach (string header in sectionHeaders)
        {
            string key = header.Trim('?', '：').Trim();
            string sectionText = ExtractSection(sbText, header);
            snapshot[key] = sectionText;
        }



        // 並列出力処理（Task.Runベース）
        PerformOutput(sbText, snapshot, txtFile, jsonFile);
        /*
                var outputTasks = new List<Task>
        {
            Task.Run(() => Console.Write(sbText)),
            Task.Run(() => File.WriteAllText(txtFile, sbText,new UTF8Encoding(false))),
            Task.Run(() => {
            try
            {
                var json = Newtonsoft.Json.JsonConvert.SerializeObject(snapshot, Newtonsoft.Json.Formatting.Indented);
                File.WriteAllText(jsonFile, json, Encoding.UTF8);
                Console.WriteLine($"JSON出力完了: {jsonFile}");
            }
            catch (Exception ex)
            {
                Console.WriteLine($"JSON出力失敗: {ex.Message}");
            }
        })


        };

                try
                {
                    Task.WaitAll(outputTasks.ToArray());
                }
                catch (Exception ex)
                {
                    Console.WriteLine("出力処理中にエラーが発生しました: " + ex.Message);
                }
        */

        // == 転送テンプレート（必要に応じて有効化） ==
        // 必要に応じて、以下の関数を有効化・改変してください

        // SMB共有フォルダへコピー
        // OutputSender.SendToSharedFolder(fileName);

        // HTTP POST送信
        // OutputSender.SendToHttpServer(fileName);

        // FTPアップロード
        // OutputSender.SendToFtpServer(fileName);

        // Syslog送信（UDP 514）
        // OutputSender.SendToSyslog(fileName);

        // SMTP送信（LAN内・ポート25・認証なし）
        // OutputSender.SendViaSmtp_LAN(fileName);



        // 終了待機 LAN出力転送するならコメントアウト推奨
        DateTime dt2 = DateTime.Now;    //時刻取得
        Console.WriteLine(dt2);
        TimeSpan timeSpan = dt2 - dt1;
        Console.Write("処理時間 :");
        Console.WriteLine(timeSpan);
        Console.WriteLine("完了しました。何かキーを押すと終了します。");
        Console.ReadKey();
    }



    //出力メソッド
    static void PerformOutput(string sbText, Dictionary<string, string> snapshot, string txtFile, string jsonFile)
    {
        // JSON出力先の判定（FAT32なら一時フォルダへ退避）
        /*
        string jsonFullPath = Path.GetFullPath(jsonFile);
        string jsonTarget = jsonFullPath;

        try
        {
            DriveInfo drive = new DriveInfo(Path.GetPathRoot(jsonFullPath));
            if (drive.DriveFormat.Equals("FAT32", StringComparison.OrdinalIgnoreCase))
            {
                Console.WriteLine("FAT32環境のため、JSON出力先を一時フォルダに変更します。");
                jsonTarget = Path.Combine(Path.GetTempPath(), Path.GetFileName(jsonFile));
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine("ドライブ判定に失敗しました（相対パスの可能性）: " + ex.Message);
            // jsonTarget はそのまま jsonFullPath を使う
        }
        */
        // 並列出力処理
        var outputTasks = new List<Task>
        {
        Task.Run(() => Console.Write(sbText)),
        Task.Run(() => File.WriteAllText(txtFile, sbText, new UTF8Encoding(false))),
        // JSON出力（Ver1.6ではコメントアウト）

        /*
        Task.Run(() => {
            try
            {
                var cleanSnapshot = snapshot.ToDictionary(
                    kv => kv.Key,
                    kv => Regex.Replace(kv.Value ?? "", @"[\x00-\x1F]", "")
                );

                var json = Newtonsoft.Json.JsonConvert.SerializeObject(cleanSnapshot, Newtonsoft.Json.Formatting.Indented);
                File.WriteAllText(jsonTarget, json, new UTF8Encoding(false));
                Console.WriteLine($"JSON出力完了: {jsonTarget}");
            }
            catch (Exception ex)
            {
                Console.WriteLine($"JSON出力失敗: {ex.GetType().Name} - {ex.Message}");
            }
        })
        */
        };

        try
        {
            Task.WaitAll(outputTasks.ToArray());
        }
        catch (AggregateException ae)
        {
            foreach (var ex in ae.InnerExceptions)
            {
                Console.WriteLine("出力タスク例外: " + ex.GetType().Name + " - " + ex.Message);
            }
        }
    }

    //辞書の初期値入力
    //この部分が唯一のマスターテーブル
    //表記順が出力順となる
    // ------------------------------
    // 拡張者向けガイド
    // 1. 新しい情報取得は OutputXXX() を追加
    // 2. 例外はメソッド内で処理すること
    // 3. mappings に1行追加するだけで反映される
    // 4. 書式:見出し,メソッド名,拡張モードフラグIsExtended, WMI負荷(0-3),処理負荷(1-4)
    // 5. WmiLevel / LoadLevel は並列段生成のための目安値
    /*
    WmiLevel の意味
     	0 = WMI 不使用
     	1 = 軽い（1?2回）
     	2 = 中程度（3?5回）
     	3 = 重い（5回以上 or Win32_ 系複数）
    LoadLevel の意味
     	1 = 軽い
     	2 = 普通
     	3 = 重い
     	4 = 最重量
    */
    // ------------------------------
    // マスターテーブル（唯一の情報源）
    static (string Comment,
            Func<string> Method,
            bool IsExtended,
            byte WmiLevel,
            byte LoadLevel)[] mappings;

    static void InitializeMethodMaps()
    {
        mappings  = new (string, Func<string>, bool,byte,byte)[]
        {
        ("PC起動時刻", OutputUptimeInfo, false, 1, 1),
        ("コンピュータ名、ドメイン、ユーザー", OutputMachineInfo, false, 0, 1),
        ("OS情報", OutputOSInfo, false, 1, 2),
        ("マザーボード情報", OutputMotherboardInfo, false, 1, 2),
        ("BIOS情報", OutputBIOSInfo, false, 1, 2),
        ("CPUとメモリー情報", OutputSystemInfo, false, 1, 3),
        ("ストレージ情報", OutputDiskInfo, false, 1, 3),
        ("GPU情報", OutputGPUInfo, false, 1, 3),
        ("ディスプレイ情報", OutputDisplayInfo, false, 0, 2),
        ("ネットワーク構成", OutputNetworkInfo, false, 1, 3),
        ("接続デバイス一覧", OutputDeviceInfo, false, 1, 4),
        ("環境変数", OutputEnvironmentVariables, false, 0, 1),
        ("Windows Update履歴", OutputUpdateHistory, false, 1, 3),
        ("最近インストールされたソフトウェア", OutputSoftwareInfo, false, 0, 4),
        ("スタートアップアプリ一覧", OutputStartupApps, false, 1, 3),
        ("サードパーティ製プロセス一覧", OutputResidentProcesses, false, 0, 3),
        ("セキュリティソフト状況", OutputSecurityStatus, false, 1, 4),
        ("FW プロファイル状態", GetFirewallProfileStatus, false, 0, 2),
        ("異常ドライバー検出", OutputDriverIssues, false, 1, 4),
        ("最近のシステムイベントログ", OutputEventLog, false, 0, 4),
        ("電源プラン", OutputPowerPlan, false, 0, 1),
        
        // 追加例（WMIを使わない軽量項目）
        // ("サンプル項目", SampleMethod, false, 0, 1),
        
        // === -E 専用 ===
        ("ユーザーアカウント一覧", OutputUserAccounts, true, 1, 3),
        ("タスクスケジューラ一覧", GetTaskSchedulerInfo, true, 0, 4),
        ("削除ソフト痕跡", OutputUninstallTraces, true, 0, 4),
        ("サービス一覧", GetFilteredServices, true, 1, 3),
        ("ファイアウォールルール", OutputFirewallRules_E, true, 0, 4),
        ("NTP同期状態",OutputNtpStatus,true, 0, 1)
        };

        MaxMethod = (byte)mappings.Length;
        sb_data = new ConcurrentDictionary<int, string>();

        for (int i = 0; i < MaxMethod; i++)
        {
            sb_data[i] = "";
        }
    }

    // --- 各情報取得メソッド ---

    // Win32 API (EnumDisplayDevices) を使用してGPUと接続モニター情報を取得
    static string OutputDisplayInfo()
    {
        var sb = new StringBuilder();
        sb.AppendLine("?? ディスプレイ情報（EnumDisplayDevicesより）：");

        try
        {
            DISPLAY_DEVICE gpu = new DISPLAY_DEVICE { cb = Marshal.SizeOf<DISPLAY_DEVICE>() };

            for (uint i = 0; EnumDisplayDevices(null, i, ref gpu, 0); i++)
            {
                sb.AppendLine($"GPU {i}: {gpu.DeviceString}");

                DISPLAY_DEVICE monitor = new DISPLAY_DEVICE { cb = Marshal.SizeOf<DISPLAY_DEVICE>() };

                if (EnumDisplayDevices(gpu.DeviceName, 0, ref monitor, 0))
                {
                    sb.AppendLine($"  -> モニター: {monitor.DeviceString}");
                    sb.AppendLine($"     PnP名: {monitor.DeviceName}");
                    break;
                }

                gpu = new DISPLAY_DEVICE { cb = Marshal.SizeOf<DISPLAY_DEVICE>() };
            }
        }
        catch (Exception ex)
        {
            sb.AppendLine($"[OutputDisplayInfo] エラー: {ex.Message}");
        }

        return sb.ToString();
    }

    // WMI (Win32_OperatingSystem) からOS情報を取得

    // OS情報
    static string OutputOSInfo()
    {
        var sb = new StringBuilder();
        // 共通ヘッダー
        sb.AppendLine("--- [PC環境リスト] ---");
        try
        {
            using (var searcher = new ManagementObjectSearcher("SELECT Caption, Version, OSArchitecture, CSDVersion FROM Win32_OperatingSystem"))
            {
                foreach (var os in searcher.Get())
                {
                    sb.AppendLine($"[OS] {os["Caption"]} {os["Version"]} ({os["OSArchitecture"]}) {os["CSDVersion"]}");
                }
            }
        }
        catch (Exception ex)
        {
            sb.AppendLine($"[OS情報取得失敗] {ex.Message}");
        }
        return sb.ToString();
    }


    // WMI (Win32_Processor, Win32_ComputerSystem, Win32_PhysicalMemory) からCPU/RAM情報を取得

    static string OutputSystemInfo()
    {
        var sb = new StringBuilder();
        sb.AppendLine("?? CPUとメモリー情報：");

        // CPU情報
        var cpuSearcher = new ManagementObjectSearcher("SELECT * FROM Win32_Processor");
        foreach (ManagementObject cpu in cpuSearcher.Get())
        {
            sb.AppendLine($"CPU: {cpu["Name"]}");
            sb.AppendLine($"コア数: {cpu["NumberOfCores"]}, スレッド数: {cpu["NumberOfLogicalProcessors"]}");
        }

        // RAM合計容量
        var memSearcher = new ManagementObjectSearcher("SELECT * FROM Win32_ComputerSystem");
        foreach (ManagementObject mem in memSearcher.Get())
        {
            // TotalPhysicalMemory はバイト単位なので、MBに変換
            ulong totalMemory = Convert.ToUInt64(mem["TotalPhysicalMemory"]);
            sb.AppendLine($"RAM合計: {totalMemory / (1024 * 1024)} MB");
        }

        // RAM速度と種類 (スロット別)
        var ramSearcher = new ManagementObjectSearcher("SELECT * FROM Win32_PhysicalMemory");
        foreach (ManagementObject ram in ramSearcher.Get())
        {
            // MemoryType (24=DDR3, 26=DDR4, 34=DDR5 など。WMIの規格値を使用)
            string memoryType = GetMemoryType(Convert.ToInt32(ram["MemoryType"]));
            ulong sizeMB = Convert.ToUInt64(ram["Capacity"]) / (1024 * 1024);
            sb.AppendLine($"RAM Slot: {ram["DeviceLocator"]}, Speed: {ram["Speed"]} MHz, Type: {memoryType}, Size: {sizeMB} MB");
        }
        return sb.ToString();
    }

    // WMIのMemoryTypeコードをDDR世代に変換 (C# 8.0以降のswitch式を使用 以前でも.csprojに追記で可能な場合有り)

    static string GetMemoryType(int type)
    {
        return type switch
        {
            24 => "DDR3",
            26 => "DDR4",
            34 => "DDR5",
            _ => "Unknown" // その他の値はUnknownとする
        };
    }


    // WMI (Win32_LogicalDisk) からストレージ情報を取得

    static string OutputDiskInfo()
    {
        var sb = new StringBuilder();
        sb.AppendLine("?? ストレージ情報 (容量と空き容量)：");

        try
        {
            var searcher = new ManagementObjectSearcher("SELECT * FROM Win32_LogicalDisk");
            foreach (ManagementObject disk in searcher.Get())
            {
                string deviceId = Clean(disk["DeviceID"]?.ToString()).ToUpper();
                string volumeName = Clean(disk["VolumeName"]?.ToString());

                int driveType = Convert.ToInt32(disk["DriveType"]);
                string typeLabel = driveType switch
                {
                    2 => "リムーバブル",
                    3 => "ローカル",
                    4 => "ネットワーク",
                    5 => "CD/DVD",
                    6 => "RAMディスク",
                    _ => "その他"
                };

                ulong totalBytes = disk["Size"] != null ? Convert.ToUInt64(disk["Size"]) : 0;
                ulong freeBytes = disk["FreeSpace"] != null ? Convert.ToUInt64(disk["FreeSpace"]) : 0;

                string totalGB = (totalBytes / (1024 * 1024 * 1024)).ToString();
                string freeGB = (freeBytes / (1024 * 1024 * 1024)).ToString();

                sb.AppendLine($"ドライブ {deviceId} - 合計: {totalGB} GB, 空き: {freeGB} GB, ボリューム名: {volumeName}, 種別: {typeLabel}");
            }
        }
        catch (Exception ex)
        {
            sb.AppendLine($"[OutputDiskInfo] エラー: {ex.Message}");
        }

        return sb.ToString();
    }


    static string Clean(string input) => input?.Replace("\0", "").Trim() ?? "";



    //接続デバイス　主にUSB
    static string OutputDeviceInfo()
    {
        var sb = new StringBuilder();
        sb.AppendLine("?? 接続されているデバイス一覧（分類・最大" + MaxDataOutput + "件）：");

        try
        {
            var searcher = new ManagementObjectSearcher("SELECT * FROM Win32_PnPEntity");
            var allDevices = new List<string>();
            var avDevices = new List<string>();

            foreach (ManagementObject device in searcher.Get())
            {
                string name = device["Name"]?.ToString();
                if (string.IsNullOrEmpty(name)) continue;

                if (name.IndexOf("Bus", StringComparison.OrdinalIgnoreCase) >= 0 ||
                    name.IndexOf("Component", StringComparison.OrdinalIgnoreCase) >= 0 ||
                    name.IndexOf("Driver", StringComparison.OrdinalIgnoreCase) >= 0 ||
                    name.IndexOf("Service", StringComparison.OrdinalIgnoreCase) >= 0 ||
                    name.IndexOf("Controller", StringComparison.OrdinalIgnoreCase) >= 0)
                {
                    continue;
                }

                if (name.IndexOf("Microphone", StringComparison.OrdinalIgnoreCase) >= 0 ||
                    name.IndexOf("Webcam", StringComparison.OrdinalIgnoreCase) >= 0 ||
                    name.IndexOf("Camera", StringComparison.OrdinalIgnoreCase) >= 0 ||
                    name.IndexOf("Audio", StringComparison.OrdinalIgnoreCase) >= 0 ||
                    name.IndexOf("DAC", StringComparison.OrdinalIgnoreCase) >= 0 ||
                    name.IndexOf("Capture", StringComparison.OrdinalIgnoreCase) >= 0 ||
                    name.IndexOf("Video", StringComparison.OrdinalIgnoreCase) >= 0)
                {
                    avDevices.Add(name);
                }
                else if (
                    name.IndexOf("Hub", StringComparison.OrdinalIgnoreCase) < 0 &&
                    (name.IndexOf("USB", StringComparison.OrdinalIgnoreCase) >= 0 ||
                     name.IndexOf("Bluetooth", StringComparison.OrdinalIgnoreCase) >= 0 ||
                     name.IndexOf("Printer", StringComparison.OrdinalIgnoreCase) >= 0 ||
                     name.IndexOf("Display", StringComparison.OrdinalIgnoreCase) >= 0))
                {
                    bool isHub =
                        name.IndexOf("Hub", StringComparison.OrdinalIgnoreCase) >= 0 ||
                        name.Contains("ハブ") ||
                        name.Contains("ルート ハブ") ||
                        name.Contains("Composite") ||
                        name.Contains("SuperSpeed USB ハブ");

                    bool isUsefulUSB =
                        name.IndexOf("Input", StringComparison.OrdinalIgnoreCase) >= 0 ||
                        name.IndexOf("Keyboard", StringComparison.OrdinalIgnoreCase) >= 0 ||
                        name.IndexOf("Mouse", StringComparison.OrdinalIgnoreCase) >= 0 ||
                        name.IndexOf("Storage", StringComparison.OrdinalIgnoreCase) >= 0 ||
                        name.IndexOf("Logitech", StringComparison.OrdinalIgnoreCase) >= 0 ||
                        name.IndexOf("DAC", StringComparison.OrdinalIgnoreCase) >= 0;

                    if (!isHub && (name.IndexOf("USB", StringComparison.OrdinalIgnoreCase) >= 0 ||
                                   name.IndexOf("Bluetooth", StringComparison.OrdinalIgnoreCase) >= 0 ||
                                   name.IndexOf("Printer", StringComparison.OrdinalIgnoreCase) >= 0 ||
                                   name.IndexOf("Display", StringComparison.OrdinalIgnoreCase) >= 0 ||
                                   isUsefulUSB))
                    {
                        allDevices.Add(name);
                    }
                }
            }

            sb.AppendLine("?? 録音・映像機器:");
            foreach (var dev in avDevices.Take(MaxDataOutput))
            {
                sb.AppendLine("- " + dev);
            }

            sb.AppendLine("??? その他の接続デバイス:");
            foreach (var dev in allDevices.Take(MaxDataOutput))
            {
                sb.AppendLine("- " + dev);
            }
        }
        catch (Exception ex)
        {
            sb.AppendLine($"[OutputDeviceInfo] エラー: {ex.Message}");
        }

        return sb.ToString();
    }


    // WMI (Win32_VideoController) からGPU情報を取得

    static string OutputGPUInfo()
    {
        var sb = new StringBuilder();
        sb.AppendLine("?? GPU情報：");

        try
        {
            var searcher = new ManagementObjectSearcher("SELECT * FROM Win32_VideoController");

            foreach (ManagementObject gpu in searcher.Get())
            {
                sb.AppendLine($"名前: {gpu["Name"]}");
                sb.AppendLine($"ドライババージョン: {gpu["DriverVersion"]}");

                // AdapterRAM (VRAM) は4GB以上でWMIの型変換に問題が出ることがあるため、チェックを追加
                if (gpu["AdapterRAM"] is ulong adapterRam)
                {
                    sb.AppendLine($"VRAM: {adapterRam / (1024 * 1024)} MB");
                }
                else
                {
                    sb.AppendLine("VRAM: (取得不可 or 4GB超過によるWMIの制限) MB");
                }

                // 現在の解像度情報
                if (gpu["CurrentHorizontalResolution"] != null && gpu["CurrentVerticalResolution"] != null)
                {
                    sb.AppendLine($"解像度: {gpu["CurrentHorizontalResolution"]} x {gpu["CurrentVerticalResolution"]}");
                }

                sb.AppendLine();
            }
        }
        catch (Exception ex)
        {
            sb.AppendLine($"[OutputGPUInfo] エラー: {ex.Message}");
        }

        return sb.ToString();
    }

    // Windowsイベントログから最近のエラーと警告を取得し、ノイズを除去

    static string OutputEventLog()
    {
        byte LogOutput = (byte)(MaxDataOutput + 5);             //最大件数を+5
        //if (expertMode) LogOutput = (byte)(LogOutput + 10);      //-E時に最大件数を+10
        var sb = new StringBuilder();
        sb.AppendLine("?? 最近のシステムイベントログ（Error/Warningのみ、10016除外 最大" + LogOutput + "件）:");

        try
        {
            using (EventLog systemLog = new EventLog("System"))
            {
                int count = 0;
                for (int i = systemLog.Entries.Count - 1; i >= 0 && count < LogOutput; i--)
                {
                    EventLogEntry entry = systemLog.Entries[i];

                    if ((entry.EntryType == EventLogEntryType.Error || entry.EntryType == EventLogEntryType.Warning)
                        && entry.Source != "DCOM" && entry.EventID != 10016)
                    {
                        sb.AppendLine($"[{entry.TimeGenerated:yyyy-MM-dd HH:mm:ss}] {entry.EntryType} - Source: {entry.Source}, EventID: {entry.EventID}");

                        string message = entry.Message.Replace('\n', ' ').Replace('\r', ' ');
                        sb.AppendLine($"    -> Message: {message.Substring(0, Math.Min(message.Length, 150))}...");

                        count++;
                    }
                }

                if (count == 0)
                {
                    sb.AppendLine("-> 過去のログに重大なError/Warningは検出されませんでした。");
                }
            }
        }
        catch (Exception ex)
        {
            sb.AppendLine($"[OutputEventLog] エラー: {ex.Message}");
        }

        return sb.ToString();
    }

    // WMI (Win32_BaseBoard) からマザーボード情報を取得

    // マザーボード情報
    static string OutputMotherboardInfo()
    {
        var sb = new StringBuilder();
        try
        {
            using (var searcher = new ManagementObjectSearcher("SELECT Manufacturer, Product, SerialNumber FROM Win32_BaseBoard"))
            {
                foreach (var board in searcher.Get())
                {
                    sb.AppendLine($"[マザーボード] {board["Manufacturer"]} {board["Product"]} SN:{board["SerialNumber"]}");
                }
            }
        }
        catch (Exception ex)
        {
            sb.AppendLine($"[マザーボード情報取得失敗] {ex.Message}");
        }
        return sb.ToString();
    }


    // WMI (Win32_OperatingSystem) からPCの起動時刻と稼働時間を取得

    static string OutputUptimeInfo()
    {
        var sb = new StringBuilder();
        sb.AppendLine();
        sb.AppendLine("?? 時間情報：");

        var searcher = new ManagementObjectSearcher("SELECT LastBootUpTime FROM Win32_OperatingSystem");
        foreach (ManagementObject os in searcher.Get())
        {
            string lastBootUp = os["LastBootUpTime"]?.ToString();
            if (!string.IsNullOrEmpty(lastBootUp))
            {
                // WMIの日付形式をC#のDateTimeに変換
                DateTime bootTime = ManagementDateTimeConverter.ToDateTime(lastBootUp);
                // TimeSpan uptime = DateTime.Now - bootTime; // 稼働時間は省略 (コメントアウト)

                sb.AppendLine($"PC起動時刻: {bootTime:yyyy-MM-dd HH:mm:ss}");
            }
        }
        return sb.ToString();
    }


    // 環境変数からPC名を取得

    // コンピュータ名、ドメイン、ユーザーなど
    static string OutputMachineInfo()
    {
        var sb = new StringBuilder();
        try
        {
            sb.AppendLine($"[マシン名] {Environment.MachineName}");
            sb.AppendLine($"[ユーザー名] {Environment.UserName}");
            sb.AppendLine($"[ドメイン] {Environment.UserDomainName}");
            sb.AppendLine($"[アーキテクチャ] {(Environment.Is64BitOperatingSystem ? "64bit" : "32bit")}");
        }
        catch (Exception ex)
        {
            sb.AppendLine($"[マシン情報取得失敗] {ex.Message}");
        }
        return sb.ToString();
    }

    //スタートアップ一覧取得
    static string OutputStartupApps()
    {
        var sb = new StringBuilder();
        sb.AppendLine("?? スタートアップアプリ一覧：");

        try
        {
            string registryPath = @"SOFTWARE\Microsoft\Windows\CurrentVersion\Run";
            using (RegistryKey key = Registry.CurrentUser.OpenSubKey(registryPath))
            {
                if (key != null)
                {
                    foreach (string name in key.GetValueNames())
                    {
                        string value = key.GetValue(name)?.ToString();
                        sb.AppendLine($"- {name}: {value}");
                    }
                }
                else
                {
                    sb.AppendLine("-> スタートアップ情報が取得できませんでした。");
                }
            }
        }
        catch (Exception ex)
        {
            sb.AppendLine($"[OutputStartupApps] エラー: {ex.Message}");
        }

        return sb.ToString();
    }



    //Windows Update履歴取得
    static string OutputUpdateHistory()
    {
        var sb = new StringBuilder();
        sb.AppendLine("?? Windows Update履歴（KB番号とインストール日）：");

        try
        {
            var searcher = new ManagementObjectSearcher("SELECT * FROM Win32_QuickFixEngineering");

            // 一旦リストに格納してソート
            var updates = new List<(string HotFixID, DateTime? InstalledOn)>();

            foreach (ManagementObject update in searcher.Get())
            {
                string hotfixId = update["HotFixID"]?.ToString();
                string installedOnStr = update["InstalledOn"]?.ToString();

                DateTime? installedOn = null;
                if (DateTime.TryParse(installedOnStr, out DateTime dt))
                    installedOn = dt;

                updates.Add((hotfixId, installedOn));
            }

            // 日付降順（新しい順）でソート
            var sorted = updates
                .OrderByDescending(u => u.InstalledOn ?? DateTime.MinValue)
                .ToList();

            // 出力
            foreach (var u in sorted)
            {
                string dateStr = u.InstalledOn?.ToString("yyyy-MM-dd") ?? "不明";
                sb.AppendLine($"- {u.HotFixID} (Installed: {dateStr})");
            }
        }
        catch (Exception ex)
        {
            sb.AppendLine($"[OutputUpdateHistory] エラー: {ex.Message}");
        }

        return sb.ToString();
    }


    /*
    static string OutputUpdateHistory()
    {
        var sb = new StringBuilder();
        sb.AppendLine("?? Windows Update履歴（KB番号とインストール日）：");

        try
        {
            var searcher = new ManagementObjectSearcher("SELECT * FROM Win32_QuickFixEngineering");
            foreach (ManagementObject update in searcher.Get())
            {
                string hotfixId = update["HotFixID"]?.ToString();
                string installedOn = update["InstalledOn"]?.ToString();
                sb.AppendLine($"- {hotfixId} (Installed: {installedOn})");
            }
        }
        catch (Exception ex)
        {
            sb.AppendLine($"[OutputUpdateHistory] エラー: {ex.Message}");
        }

        return sb.ToString();
    }
    */

    // NIC構成取得（Ver1.4+ 製造元付き・MAC照合方式）
    static string OutputNetworkInfo()
    {
        var sb = new StringBuilder();
        sb.AppendLine("?? ネットワーク構成（System.Net + WMIより）：");

        // WMIから MAC → 製造元 の辞書を作成
        var vendorMap = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
        try
        {
            var wmiSearcher = new ManagementObjectSearcher("SELECT * FROM Win32_NetworkAdapter WHERE PhysicalAdapter = TRUE");
            foreach (ManagementObject nic in wmiSearcher.Get())
            {
                string mac = nic["MACAddress"]?.ToString();
                string manufacturer = nic["Manufacturer"]?.ToString();
                if (!string.IsNullOrEmpty(mac) && !string.IsNullOrEmpty(manufacturer))
                {
                    string normalizedMac = mac.Replace(":", "").Replace("-", "").ToUpperInvariant();
                    vendorMap[normalizedMac] = manufacturer;
                }
            }
        }
        catch
        {
            sb.AppendLine("-> NIC製造元情報の取得に失敗しました（WMIアクセス不可）");
        }

        // System.Net.NetworkInformation でNIC状態を取得
        var interfaces = System.Net.NetworkInformation.NetworkInterface.GetAllNetworkInterfaces();

        foreach (var ni in interfaces)
        {
            if (ni.NetworkInterfaceType == NetworkInterfaceType.Loopback) continue;
            var props = ni.GetIPProperties();
            var ipList = props.UnicastAddresses.Select(ip => ip.Address.ToString()).ToList();
            var dnsList = props.DnsAddresses.Select(dns => dns.ToString()).ToList();
            var gatewayList = props.GatewayAddresses.Select(g => g.Address.ToString()).ToList();

            string mac = ni.GetPhysicalAddress().ToString().ToUpperInvariant();

            sb.AppendLine($"NIC: {ni.Name}");

            if (!string.IsNullOrEmpty(mac) && vendorMap.TryGetValue(mac, out string vendor))
            {
                sb.AppendLine($"  製造元: {vendor}");
            }
            else
            {
                sb.AppendLine($"  製造元: (取得不可 or WMI照合失敗)");
            }

            sb.AppendLine($"  種別: {ni.NetworkInterfaceType}");
            sb.AppendLine($"  状態: {ni.OperationalStatus}");
            sb.AppendLine($"  MAC: {mac}");
            sb.AppendLine($"  リンク速度: {ni.Speed / 1_000_000} Mbps");
            sb.AppendLine($"  IP: {string.Join(", ", ipList)}");
            sb.AppendLine($"  DNS: {string.Join(", ", dnsList)}");
            sb.AppendLine($"  Gateway: {string.Join(", ", gatewayList)}");
        }
        return sb.ToString();
    }


    static string OutputResidentProcesses()
    {
        var sb = new StringBuilder();
        sb.AppendLine("?? サードパーティ製プロセス一覧（常駐候補）：");

        var windowsPaths = new[]
        {
           "C:\\Windows",
           "C:\\Program Files\\WindowsApps",
           "C:\\Program Files\\Microsoft Visual Studio",
           //"C:\\Users\\<USERNAME>\\source\\repos", // 自作実行ファイル
           //"C:\\Users\\<USERNAME>\\AppData\\Local\\Microsoft", // BingSvcなど
           // 必要に応じて追加
        };
        var processes = Process.GetProcesses();

        // グループ化用の辞書：フォルダパス → プロセス一覧
        var groups = new Dictionary<string, List<string>>(StringComparer.OrdinalIgnoreCase);

        foreach (var proc in processes)
        {
            try
            {
                string name = proc.ProcessName;
                string path = proc.MainModule?.FileName ?? null;

                if (string.IsNullOrEmpty(path)) continue;

                // Windows標準プロセスを除外
                if (windowsPaths.Any(p => path.StartsWith(p, StringComparison.OrdinalIgnoreCase)))
                    continue;

                string folder = Path.GetDirectoryName(path) ?? "(不明なフォルダ)";
                string entry = $"- {name}: {path}";

                if (!groups.ContainsKey(folder))
                    groups[folder] = new List<string>();

                groups[folder].Add(entry);
            }
            catch
            {
                // アクセス拒否などは無視
            }
        }

        // 出力（フォルダ単位でグループ化）
        foreach (var kv in groups.OrderBy(k => k.Key))
        {
            sb.AppendLine($"\n■ {kv.Key}：");
            foreach (var line in kv.Value)
            {
                sb.AppendLine(line);
            }
        }
        return sb.ToString();
    }
    //セキュリティソフトの常駐状況
    static string OutputSecurityStatus()
    {
        var sb = new StringBuilder();
        sb.AppendLine("?? セキュリティソフトの常駐状況（推定）：");

        try
        {
            var searcher = new ManagementObjectSearcher(@"root\SecurityCenter2", "SELECT * FROM AntiVirusProduct");
            foreach (ManagementObject av in searcher.Get())
            {
                sb.AppendLine($"- 製品名: {av["displayName"]}");
                sb.AppendLine($"  状態: {av["productState"]}"); // 数値コード（詳細はMS仕様）
            }
        }
        catch
        {
            sb.AppendLine("-> セキュリティセンター情報が取得できませんでした。");
        }

        // サードパーティ製の推定（プロセス名から）
        var knownAV = new[] { "eset", "avast", "norton", "mcafee", "kaspersky", "trend", "bitdefender" };
        var processes = Process.GetProcesses();

        var detected = processes
            .Where(p => knownAV.Any(av => p.ProcessName.ToLower().Contains(av)))
            .Select(p => p.ProcessName)
            .Distinct()
            .ToList();

        if (detected.Count > 0)
        {
            sb.AppendLine("-> サードパーティ製セキュリティソフトの常駐候補：");
            foreach (var name in detected)
            {
                sb.AppendLine($"- {name}");
            }
        }
        else
        {
            sb.AppendLine("-> サードパーティ製セキュリティソフトは検出されませんでした。");
        }
        return sb.ToString();
    }

    // BIOS情報
    static string OutputBIOSInfo()
    {
        var sb = new StringBuilder();
        try
        {
            using (var searcher = new ManagementObjectSearcher("SELECT Manufacturer, SMBIOSBIOSVersion, ReleaseDate FROM Win32_BIOS"))
            {
                foreach (var bios in searcher.Get())
                {
                    sb.AppendLine($"[BIOS] {bios["Manufacturer"]} {bios["SMBIOSBIOSVersion"]} ({bios["ReleaseDate"]})");
                }
            }
        }
        catch (Exception ex)
        {
            sb.AppendLine($"[BIOS情報取得失敗] {ex.Message}");
        }
        return sb.ToString();
    }

    //異常ドライバー検出 収集ロジック
    static List<(string Name, string Status, string Signature, int ErrorCode)> CollectDriverIssues()
    {

        var issues = new List<(string, string, string, int)>();

        try
        {
            using (var searcher = new ManagementObjectSearcher("SELECT * FROM Win32_PnPEntity"))
            {
                foreach (ManagementObject obj in searcher.Get())
                {
                    string name = obj["Name"]?.ToString() ?? "（名称不明）";
                    int errorCode = Convert.ToInt32(obj["ConfigManagerErrorCode"] ?? 0);

                    if (name.ToLower().Contains("unknown") || errorCode != 0)
                    {
                        issues.Add((name, "不明またはエラー", "不明", errorCode));
                    }
                }
            }

            using (var searcher = new ManagementObjectSearcher("SELECT * FROM Win32_PnPSignedDriver"))
            {
                foreach (ManagementObject obj in searcher.Get())
                {
                    string name = obj["DeviceName"]?.ToString() ?? "（名称不明）";
                    bool isSigned = Convert.ToBoolean(obj["IsSigned"] ?? true);

                    if (!isSigned)
                    {
                        issues.Add((name, "未署名", "なし", 0));
                    }
                }
            }
        }
        catch (Exception ex)
        {
            issues.Add(($"（ドライバー情報取得失敗: {ex.Message}）", "取得失敗", "不明", -1));
        }

        return issues;
    }

    //異常ドライバー検出 出力
    static string OutputDriverIssues()
    {
        var sb = new StringBuilder();
        var issues = CollectDriverIssues();
        sb.AppendLine("?? 異常ドライバー検出:");
        if (issues.Count == 0) return sb.ToString();
        try
        {
            

            if (!expertMode)
                sb.AppendLine("※ 不明なデバイスや未署名ドライバーを検出しました。詳細は -E モードで確認できます。");

            foreach (var issue in issues)
            {
                if (expertMode)
                {
                    sb.AppendLine($"- {issue.Name}");
                    sb.AppendLine($"  状態: {issue.Status}");
                    sb.AppendLine($"  署名: {issue.Signature}");
                    sb.AppendLine($"  エラーコード: {issue.ErrorCode}");
                }
                else
                {
                    sb.AppendLine($"- {issue.Name}");
                }
            }
        }
        catch (Exception ex)
        {
            sb.AppendLine("?? 異常ドライバー検出:");
            sb.AppendLine("※ ドライバー情報の取得中にエラーが発生しました。");
            sb.AppendLine($"エラー内容: {ex.Message}");
        }
        return sb.ToString();
    }

    // タスクスケジューラ登録一覧（不正ソフトが居るかも）
    // ============================================================================
    // ObservePC: タスクスケジューラ登録一覧（XML解析 + LIST/Vモード + 状態＆トリガー型）
    // ============================================================================

    static string GetTaskSchedulerInfo()
    {
        var sb = new StringBuilder();
        sb.AppendLine("?? タスクスケジューラ登録一覧（整形済・フィルタ＋状態＋トリガー型対応）：");

        try
        {
            string xmlOutput = RunCmd("schtasks /query /xml ONE");

            if (!string.IsNullOrWhiteSpace(xmlOutput) && xmlOutput.Contains("<Task"))
            {
                sb.AppendLine(ParseTasksFromXml(xmlOutput));
            }
            else
            {
                sb.AppendLine("-> XML解析不可。LIST/Vモードで再試行します。");
                sb.AppendLine(GetTaskSchedulerListFallback());
            }
        }
        catch
        {
            sb.AppendLine("-> XML解析失敗。LIST/Vモードで再試行します。");
            sb.AppendLine(GetTaskSchedulerListFallback());
        }

        return sb.ToString();
    }

    // ============================================================================
    // XML解析ロジック
    // ============================================================================
    static string ParseTasksFromXml(string xml)
    {
        var result = new StringBuilder();
        int count = 1;
        var matches = Regex.Split(xml, @"(?=<Task )");

        foreach (var block in matches)
        {
            if (!block.Contains("<Task")) continue;

            string name = ExtractXmlTag(block, "URI");
            string author = ExtractXmlTag(block, "Author");
            string cmd = ExtractXmlTag(block, "Command");
            string enabled = ExtractXmlTag(block, "Enabled");
            string triggerType = ExtractTriggerType(block);
            string start = ExtractXmlTag(block, "StartBoundary");

            string trigger = $"{triggerType} {(string.IsNullOrEmpty(start) ? "" : "開始: " + start)}";

            if (string.IsNullOrWhiteSpace(name)) continue;
            if (IsSystemTask(name, author, cmd)) continue;

            string status = enabled == "false" ? "無効" : "有効";

            result.AppendLine($"■ タスク {count}: {name}");
            result.AppendLine($"  状態: {status}");
            result.AppendLine($"  登録者: {author}");
            result.AppendLine($"  トリガー: {trigger}");
            result.AppendLine($"  実行コマンド: {cmd}");
            result.AppendLine();
            count++;
        }

        if (count == 1)
            result.AppendLine("-> 登録されたタスクが見つかりませんでした。");

        return result.ToString();
    }

    static string ExtractXmlTag(string text, string tag)
    {
        var m = Regex.Match(text, $"<{tag}>(.*?)</{tag}>", RegexOptions.Singleline);
        return m.Success ? m.Groups[1].Value.Trim() : "";
    }

    // トリガー型（LogonTrigger, CalendarTrigger など）を抽出
    static string ExtractTriggerType(string xmlBlock)
    {
        var m = Regex.Match(xmlBlock, @"<Triggers>.*?<(\w+?)>", RegexOptions.Singleline);
        return m.Success ? m.Groups[1].Value.Trim() : "(取得不可)";
    }

    // ============================================================================
    // LIST/Vフォールバックモード
    // ============================================================================
    static string GetTaskSchedulerListFallback()
    {
        var sb = new StringBuilder();
        sb.AppendLine("?? タスクスケジューラ登録一覧（LIST/Vモード・フィルタ適用）：");

        try
        {
            var output = RunCmd("schtasks /query /fo LIST /v");
            if (string.IsNullOrWhiteSpace(output))
            {
                sb.AppendLine("-> タスク情報の取得に失敗しました。");
                return sb.ToString();
            }

            var blocks = Regex.Split(output, @"\r?\n\r?\n").Where(b => b.Contains("タスク名:")).ToList();
            int count = 1;

            foreach (var block in blocks)
            {
                var (name, author, trigger, cmd, state) = ParseTaskBlock(block);

                if (IsSystemTask(name, author, cmd))
                    continue;

                if (name == "(取得不可)" && cmd == "(取得不可)")
                    continue;

                sb.AppendLine($"■ タスク {count}: {name}");
                sb.AppendLine($"  状態: {state}");
                sb.AppendLine($"  登録者: {author}");
                sb.AppendLine($"  トリガー: {trigger}");
                sb.AppendLine($"  実行コマンド: {cmd}");
                sb.AppendLine();
                count++;
            }

            if (count == 1)
                sb.AppendLine("-> 登録されたタスクが見つかりませんでした。");
        }
        catch (Exception ex)
        {
            sb.AppendLine("-> LIST/Vモード解析エラー: " + ex.Message);
        }

        return sb.ToString();
    }

    static (string Name, string Author, string Trigger, string Command, string State) ParseTaskBlock(string block)
    {
        string name = "(取得不可)";
        string author = "(取得不可)";
        string trigger = "(取得不可)";
        string command = "(取得不可)";
        string state = "(取得不可)";

        foreach (var raw in block.Split(new[] { "\r\n", "\n" }, StringSplitOptions.None))
        {
            string l = raw.Trim();
            if (string.IsNullOrEmpty(l)) continue;

            if (l.StartsWith("タスク名:", StringComparison.OrdinalIgnoreCase))
                name = l.Split(new[] { ':' }, 2)[1].Trim();

            else if (l.StartsWith("作成者:", StringComparison.OrdinalIgnoreCase))
                author = l.Split(new[] { ':' }, 2)[1].Trim();

            else if (l.StartsWith("スケジュールの種類:", StringComparison.OrdinalIgnoreCase))
                trigger = l.Split(new[] { ':' }, 2)[1].Trim();

            else if (l.StartsWith("トリガー:", StringComparison.OrdinalIgnoreCase))
                trigger = l.Split(new[] { ':' }, 2)[1].Trim();

            else if (l.StartsWith("実行するタスク:", StringComparison.OrdinalIgnoreCase))
                command = l.Split(new[] { ':' }, 2)[1].Trim();

            else if (l.StartsWith("実行コマンド:", StringComparison.OrdinalIgnoreCase))
                command = l.Split(new[] { ':' }, 2)[1].Trim();

            else if (l.StartsWith("状態:", StringComparison.OrdinalIgnoreCase))
                state = l.Split(new[] { ':' }, 2)[1].Trim();
        }

        return (name, author, trigger, command, state);
    }

    // ============================================================================
    // システム・大手製アプリ除外フィルタ
    // ============================================================================
    static bool IsSystemTask(string name, string author, string cmd)
    {
        string n = (name ?? "").ToLower();
        string a = (author ?? "").ToLower();
        string c = (cmd ?? "").ToLower();

        string[] keywords =
        {
        "\\microsoft\\", "\\windows\\", "microsoft", "intel", "amd",
        "nvidia", "realtek", "system32", "languagecomponentsinstaller",
        "telemetry", "devicecensus", "edgeupdate", "defrag", "appx"
    };

        if (keywords.Any(k => n.Contains(k) || a.Contains(k) || c.Contains(k)))
            return true;

        if (author == "(取得不可)" && cmd == "(取得不可)")
            return true;

        return false;
    }

    // ============================================================================
    // CMD 実行ユーティリティ
    // ============================================================================
    static string RunCmd(string cmd)
    {
        using (var p = new System.Diagnostics.Process())
        {
            p.StartInfo.FileName = "cmd.exe";
            p.StartInfo.Arguments = "/c " + cmd;
            p.StartInfo.RedirectStandardOutput = true;
            p.StartInfo.RedirectStandardError = true;
            p.StartInfo.UseShellExecute = false;
            p.StartInfo.CreateNoWindow = true;
            p.StartInfo.StandardOutputEncoding = Encoding.GetEncoding("shift_jis");
            p.StartInfo.StandardErrorEncoding = Encoding.GetEncoding("shift_jis");
            p.Start();

            string output = p.StandardOutput.ReadToEnd();
            p.WaitForExit();
            return output;
        }
    }


    // 削除ソフトの痕跡（イベントログ＋レジストリ）
    // 目的：ユーザーがアンインストールしたはずなのに、レジストリに痕跡が残っているソフトを検出する
    // 方法：InstallDateが空のレジストリキーや、イベントログの削除メッセージを元に推定
    static string OutputUninstallTraces()
    {
        var sb = new StringBuilder();
        sb.AppendLine("?? 削除ソフト痕跡（レジストリより推定）:");
        sb.AppendLine("※ 以下は「インストール履歴に痕跡が残っているが、削除された可能性があるソフト」です。");
        HashSet<string> detectedApps = new HashSet<string>();

        string[] uninstallKeys = {
        @"SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall",
        @"SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall"
    };

        foreach (string rootKey in uninstallKeys)
        {
            using (RegistryKey key = Registry.LocalMachine.OpenSubKey(rootKey))
            {
                if (key == null) continue;

                foreach (string subkeyName in key.GetSubKeyNames())
                {
                    using (RegistryKey subkey = key.OpenSubKey(subkeyName))
                    {
                        string displayName = subkey?.GetValue("DisplayName")?.ToString();
                        string uninstallDate = subkey?.GetValue("InstallDate")?.ToString();

                        if (!string.IsNullOrEmpty(displayName) &&
                            string.IsNullOrEmpty(uninstallDate) &&
                            IsLikelyUserApp(displayName) &&
                            !detectedApps.Contains(displayName))
                        {
                            sb.AppendLine($"- {displayName}");
                            detectedApps.Add(displayName);
                        }
                    }
                }
            }
        }

        sb.AppendLine();
        sb.AppendLine("?? 削除ソフト痕跡（イベントログより確定）:");
        sb.AppendLine("※ 以下は Windows インストーラーの削除記録に基づく一覧です。日時は削除実行時点を示します。");
        try
        {
            EventLog appLog = new EventLog("Application");
            int count = 0;
            foreach (EventLogEntry entry in appLog.Entries)
            {
                if (entry.Source == "MsiInstaller" && entry.EntryType == EventLogEntryType.Information)
                {
                    if (entry.Message.Contains("削除") || entry.Message.Contains("アンインストール"))
                    {
                        string msg = entry.Message;
                        string productName = ExtractBetween(msg, "製品名: ", "、");
                        string time = entry.TimeGenerated.ToString("yyyy/MM/dd HH:mm");
                        if (!string.IsNullOrEmpty(productName) && IsLikelyUserApp(productName))
                        {
                            sb.AppendLine($"- {productName}（ {time}）");
                        }
                    }

                }
            }
        }
        catch (Exception ex)
        {
            sb.AppendLine($"（イベントログ取得失敗: {ex.Message}）");
        }
        return sb.ToString();
    }


    static bool IsLikelyUserApp(string name)
    {
        string[] ignoreKeywords = {
          "update", "security update", "hotfix", "patch", "language pack",
          "redistributable", "runtime", "sdk", "driver", "framework", "help viewer",
         "service pack", "help 更新", "visual c++", ".net", "targeting pack",
         "host fx resolver", "templates", "manifest", "apphost pack", "workload",
         "emscripten", "mono", "maui", "macos", "ios", "android", "tvos", "xamarin",
        "intellisense", "extension sdk", "desktop runtime", "toolset", "clickonce",
           "vs_", "windows sdk", "diagnostic", "crt", "lib", "headers", "tools"
};
        if (string.IsNullOrEmpty(name)) return false;
        string lowerName = name.ToLower();
        return !ignoreKeywords.Any(k => lowerName.Contains(k));
    }

    static string ExtractBetween(string source, string start, string end)
    {
        int startIndex = source.IndexOf(start);
        if (startIndex == -1) return null;
        startIndex += start.Length;
        int endIndex = source.IndexOf(end, startIndex);
        if (endIndex == -1) return null;
        return source.Substring(startIndex, endIndex - startIndex).Trim();
    }


    //ユーザー情報
    static string OutputUserAccounts()
    {
        var sb = new StringBuilder();
        sb.AppendLine("?? ユーザーアカウント一覧：");

        try
        {
            var searcher = new ManagementObjectSearcher("SELECT Name, Disabled, Status FROM Win32_UserAccount WHERE LocalAccount = TRUE");
            foreach (ManagementObject user in searcher.Get())
            {
                string name = user["Name"]?.ToString() ?? "(取得不可)";
                string status = user["Status"]?.ToString() ?? "(状態不明)";
                bool disabled = user["Disabled"] is bool d && d;

                sb.AppendLine($"■ ユーザー: {name}");
                sb.AppendLine($"  状態: {status}");
                sb.AppendLine($"  有効: {(disabled ? "無効" : "有効")}");
            }
        }
        catch (Exception ex)
        {
            sb.AppendLine("-> ユーザー情報の取得に失敗しました: " + ex.Message);
        }
        return sb.ToString();
    }

    // レジストリ (HKEY_LOCAL_MACHINE) からインストール済みソフトウェア情報を取得
    static string OutputSoftwareInfo()
    {
        var sb = new StringBuilder();
        var apps = new List<InstalledApp>();
        // 64bit/32bit両方のレジストリパスから情報を検索
        string[] registryPaths = new[]
        {
            @"SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall", // 64bitアプリケーション
            @"SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall" // 32bitアプリケーション (WOW64)
        };

        foreach (string path in registryPaths)
        {
            // HKEY_LOCAL_MACHINE (ローカルPC全体の設定) を開く
            using (RegistryKey key = Registry.LocalMachine.OpenSubKey(path))
            {
                if (key == null) continue;

                foreach (string subkeyName in key.GetSubKeyNames())
                {
                    using (RegistryKey subkey = key.OpenSubKey(subkeyName))
                    {
                        string name = subkey?.GetValue("DisplayName") as string;
                        string dateStr = subkey?.GetValue("InstallDate") as string; // yyyyMMdd形式で格納されていることが多い

                        if (!string.IsNullOrEmpty(name))
                        {
                            DateTime? installDate = null;
                            // 日付文字列のパースを試みる
                            if (!string.IsNullOrEmpty(dateStr) && dateStr.Length == 8)
                            {
                                if (DateTime.TryParseExact(dateStr, "yyyyMMdd", CultureInfo.InvariantCulture, DateTimeStyles.None, out DateTime parsedDate))
                                {
                                    installDate = parsedDate;
                                }
                            }
                            // "Update" や "Hotfix" を含むノイズをフィルタリング
                            if (!name.Contains("Update") && !name.Contains("Hotfix"))
                            {
                                apps.Add(new InstalledApp { Name = name, InstallDate = installDate });
                            }
                        }
                    }
                }
            }
        }

        // インストール日で降順に並べ替え、最新の20件を抽出
        var recentApps = apps
            .Where(a => a.InstallDate.HasValue) // 日付が取得できたもののみ
            .OrderByDescending(a => a.InstallDate.Value)
            .Take(MaxDataOutput)
            .ToList();

        sb.AppendLine("?? 最近インストールされたソフトウェア（最大" + MaxDataOutput + "件）:");
        foreach (var app in recentApps)
        {
            sb.AppendLine($"{app.InstallDate.Value:yyyy-MM-dd} - {app.Name}");
        }
        return sb.ToString();
    }


    static string OutputEnvironmentVariables()
    {

        var sb = new StringBuilder();
        sb.AppendLine("?? 環境変数（" + (expertMode ? "詳細表示（-Eモード）" : "ユーザー環境変数のみ") + "）：");

        // ユーザー環境変数
        var userVariables = Environment.GetEnvironmentVariables(EnvironmentVariableTarget.User);
        sb.AppendLine("■ ユーザー環境変数:");
        foreach (DictionaryEntry entry in userVariables)
        {
            string key = entry.Key?.ToString();
            string value = entry.Value?.ToString();
            if (!string.IsNullOrEmpty(key) && !string.IsNullOrEmpty(value))
            {
                sb.AppendLine($"{key}: {value}");
            }
        }

        // -Eモード時のみシステム環境変数を表示
        if (expertMode)
        {
            var systemVariables = Environment.GetEnvironmentVariables(EnvironmentVariableTarget.Machine);
            sb.AppendLine("■ システム環境変数:");

            // Pathを分割して最大5件まで表示
            if (systemVariables["Path"] is string path)
            {
                sb.AppendLine("Path（最大5件）:");
                string[] paths = path.Split(';');
                foreach (string p in paths.Take(5))
                {
                    sb.AppendLine($"  - {p}");
                }
            }

            // ComSpecなどの重要変数
            if (systemVariables["ComSpec"] is string comspec)
            {
                sb.AppendLine($"ComSpec: {comspec}");
            }
        }
        return sb.ToString();
    }

    static string GetFilteredServices()
    {
        var sb = new StringBuilder();

        // サマリー用カウンタ
        int total = 0, running = 0, stopped = 0, disabled = 0;
        int count = 0;

        // 例外リスト（残したいMicrosoft系サービス）
        var exceptionList = new HashSet<string>(StringComparer.OrdinalIgnoreCase)
    {
        "WSearch", "EventLog"
    };

        try
        {
            var searcher = new ManagementObjectSearcher("SELECT * FROM Win32_Service");
            foreach (ManagementObject service in searcher.Get())
            {
                total++;

                string name = service["DisplayName"]?.ToString() ?? service["Name"]?.ToString();
                string processName = service["Name"]?.ToString() ?? "";
                string state = service["State"]?.ToString();
                string startMode = service["StartMode"]?.ToString();
                string status = service["Status"]?.ToString();

                // サマリー集計
                if (state == "Running")
                    running++;
                else if (startMode == "Disabled")
                    disabled++;
                else
                    stopped++;

                // 異常サービス抽出
                bool isSuspicious = (state == "Stopped" && startMode == "Auto") || status != "OK";
                bool isSystemService =
                    (name.StartsWith("Microsoft", StringComparison.OrdinalIgnoreCase) ||
                     name.StartsWith("Windows", StringComparison.OrdinalIgnoreCase) ||
                     processName.StartsWith("Microsoft", StringComparison.OrdinalIgnoreCase) ||
                     processName.StartsWith("Windows", StringComparison.OrdinalIgnoreCase)) &&
                    !exceptionList.Contains(processName);

                if (isSuspicious && !isSystemService)
                {
                    if (count == 0)
                        sb.AppendLine("?? サービス一覧（異常・停止・無効のみ 最大30件）:");
                    sb.AppendLine($"- {name}: {state} ({startMode})");
                    count++;
                    if (count >= 30) break;
                }
            }

            // サマリー出力
            sb.Insert(0, $"稼働中: {running} 件\n停止中: {stopped} 件\n無効: {disabled} 件\n");
            sb.Insert(0, $"全サービス数: {total} 件\n");
            sb.Insert(0, "?? サービスサマリー：");

            if (count == 0)
            {
                sb.AppendLine("異常なサービスは検出されませんでした。");
            }
        }
        catch
        {
            sb.AppendLine("サービス一覧の取得に失敗しました。");
        }

        return sb.ToString();

    }







    //セクション抽出関数 簡易JSON用
    static string ExtractSection(string fullText, string header)
    {
        int start = fullText.IndexOf(header);
        if (start < 0) return "";

        int nextHeader = fullText.IndexOf("?? ", start + header.Length);
        if (nextHeader < 0) nextHeader = fullText.Length;

        return fullText.Substring(start, nextHeader - start).Trim();
    }

    //ファイアウォール概要
    static string GetFirewallProfileStatus()
    {
        var sb = new StringBuilder();
        sb.AppendLine("?? ファイアウォールプロファイル状態:");

        try
        {
            var scope = new ManagementScope(@"\\.\root\StandardCimv2");
            var query = new ObjectQuery("SELECT * FROM MSFT_NetFirewallProfile");
            var searcher = new ManagementObjectSearcher(scope, query);

            foreach (ManagementObject profile in searcher.Get())
            {
                string name = profile["Name"]?.ToString();
                string enabled = profile["Enabled"]?.ToString() == "1" ? "有効" : "無効";
                string inbound = profile["DefaultInboundAction"]?.ToString() == "1" ? "許可" : "ブロック";
                string outbound = profile["DefaultOutboundAction"]?.ToString() == "1" ? "許可" : "ブロック";

                sb.AppendLine($"- {name}: {enabled}（受信: {inbound}, 送信: {outbound}）");
            }
        }
        catch (Exception ex)
        {
            sb.AppendLine($"[GetFirewallProfileStatus] エラー: {ex.Message}");
        }

        return sb.ToString();
    }

    //ユーザー・アプリが設定したファイアウォールルール(-Eモード専用)
    static string OutputFirewallRules_E()
    {
        var sb = new StringBuilder();
        string output = RunCommand("netsh", "advfirewall firewall show rule name=all");
        List<string> customRules = ExtractCustomFirewallRules(output);

        //Console.WriteLine("== ユーザー/アプリが設定したファイアウォールルール ==");
        sb.AppendLine("?? ユーザー/アプリが設定したファイアウォールルール:");
        foreach (var rule in customRules)
        {
            //sb.AppendLine(rule);
            sb.AppendLine($"- {rule}:");
            //sb.AppendLine();
            //Console.WriteLine(rule);
            //Console.WriteLine(); // セパレータ
        }
        //Console.WriteLine(sb);
        return sb.ToString();
    }

    static string RunCommand(string fileName, string arguments)
    {
        var psi = new ProcessStartInfo
        {
            FileName = fileName,
            Arguments = arguments,
            RedirectStandardOutput = true,
            UseShellExecute = false,
            CreateNoWindow = true,
            StandardOutputEncoding = Encoding.UTF8
        };

        using (var process = Process.Start(psi))
        {
            string output = process.StandardOutput.ReadToEnd();
            process.WaitForExit();
            return output;
        }
    }

    static List<string> ExtractCustomFirewallRules(string netshOutput)
    {
        var results = new List<string>();
        var ruleNames = new HashSet<string>();

        try
        {
            var blocks = netshOutput.Split(new[] { "\r\n\r\n" }, StringSplitOptions.RemoveEmptyEntries);

            foreach (var block in blocks)
            {
                string lower = block.ToLower();
                if (IsExcluded(lower)) continue;

                var lines = block.Split(new[] { "\r\n" }, StringSplitOptions.None);
                var ruleLine = Array.Find(lines, line => line.TrimStart().StartsWith("規則名:"));
                if (ruleLine != null)
                {
                    string ruleName = ruleLine.Substring(ruleLine.IndexOf("規則名:") + "規則名:".Length).Trim();
                    ruleNames.Add(ruleName);
                }
            }

            //Console.WriteLine("== ユーザー/アプリが設定したファイアウォールルール ==\n");
            foreach (var name in ruleNames)
            {
                //Console.WriteLine($"- {name}");
            }

            results.AddRange(ruleNames);
        }
        catch (Exception ex)
        {
            Console.WriteLine($"[ExtractCustomFirewallRules] エラー: {ex.Message}");
        }

        return results;
    }



    static bool IsExcluded(string lower)
    {
        return
            lower.Contains("有効:") && lower.Contains("いいえ") ||
            lower.Contains("規則名:") && (
                lower.Contains("microsoft") ||
                lower.Contains("windows") ||
                lower.Contains("xbox") ||
                lower.Contains("game bar") ||
                lower.Contains("delivery optimization") ||
                lower.Contains("print 3d") ||
                lower.Contains("widgets") ||
                lower.Contains("web experience") ||
                lower.Contains("コア ネットワーク") ||
                lower.Contains("リモート アシスタンス") ||
                lower.Contains("ms-resource:") ||
                lower.Contains("@{")
            ) ||
            lower.Contains("グループ:") && (
                lower.Contains("microsoft") ||
                lower.Contains("windows") ||
                lower.Contains("xbox") ||
                lower.Contains("game bar") ||
                lower.Contains("delivery optimization") ||
                lower.Contains("print 3d") ||
                lower.Contains("widgets") ||
                lower.Contains("web experience") ||
                lower.Contains("コア ネットワーク") ||
                lower.Contains("リモート アシスタンス")
            );
    }

    //NTP情報
    static string OutputNtpStatus()
    {
        var sb = new StringBuilder();
        sb.AppendLine("?? NTP同期状態：");

        try
        {
            //sb.AppendLine(RunCmd("w32tm /query /status"));  //
            sb.AppendLine(RunCmd("w32tm /query /peers"));  //コマンド
        }
        catch
        {
            return "NTP同期情報: 取得失敗";
        }

        if (string.IsNullOrWhiteSpace(sb.ToString()))
            return "NTP同期情報: 取得失敗";

        return sb.ToString();
    }


    //電源プラン
    static string OutputPowerPlan()
    {
        var sb = new StringBuilder();
        sb.AppendLine("?? 電源プラン：");

        try
        {
            sb.AppendLine(RunCmd("powercfg /getactivescheme"));
        }
        catch
        {
            return "電源プラン: 取得失敗";
        }

        if (string.IsNullOrWhiteSpace(sb.ToString()))
            return "電源プラン: 取得失敗";

        return sb.ToString();
    }





    //処理速度と安定性の両立を目指す多段並列処理
    //段生成アルゴリズム
    //WMI使用数 <= 3
    //負荷合計 <= 25
    //を基準に自動で段を作る。
    static List<List<int>> BuildParallelPhases(bool expertMode)
    {
        // 処理が軽い順にソート（Load → WMI）
        var indices = Enumerable.Range(0, MaxMethod)
            .Where(i => expertMode || !mappings[i].IsExtended)
            .OrderBy(i => mappings[i].LoadLevel)
            .ThenBy(i => mappings[i].WmiLevel)
            .ToList();

        var phases = new List<List<int>>();
        var current = new List<int>();

        byte wmi = 0;
        byte load = 0;

        foreach (var i in indices)
        {
            var m = mappings[i];

            bool canAdd =
                (wmi + m.WmiLevel <= 3) &&
                (load + m.LoadLevel <= 25);

            if (!canAdd)
            {
                phases.Add(current);
                current = new List<int>();
                wmi = 0;
                load = 0;
            }

            current.Add(i);
            wmi += m.WmiLevel;
            load += m.LoadLevel;
        }

        if (current.Count > 0)
            phases.Add(current);

        return phases;
    }



    // PC内部の情報源から多段並列処理でデーター収集(-E用)
    //自動並列実行
    static async Task<string> GetAllSystemDataAsync_E(bool expertMode)
    {
        var sb = new StringBuilder();
        var phases = BuildParallelPhases(expertMode);
        int phaseNum = 1;

        foreach (var phase in phases)
        {
            //sb.AppendLine($"==== Phase {phaseNum} ====");
            Console.WriteLine($"==== Phase {phaseNum} ====");
            var tasks = phase.Select(index =>
               Task.Run(() => sb_data[index] = mappings[index].Method())
            ).ToArray();

            await Task.WhenAll(tasks);

            phaseNum++;
        }

        return sb.ToString();
    }

    // PC内部の情報源から全ての取得メソッドを無制限並列処理でデーター収集。最速モード(-F用)
    static async Task<string> GetAllSystemDataFastAsync(bool expertMode)
    {
        var sb = new StringBuilder();

        var tasks = new List<Task>();

        for (int i = 0; i < MaxMethod; i++)
        {
            var m = mappings[i];

            if (!expertMode && m.IsExtended)
                continue;

            int index = i;

            tasks.Add(Task.Run(() =>
            {
                sb_data[index] = m.Method();
            }));
        }

        await Task.WhenAll(tasks);

        //sb.AppendLine("==== FastMode: 全メソッド無制限並列実行 ====");
        Console.WriteLine("==== FastMode: 全メソッド無制限並列実行 ====");

        return sb.ToString();
    }
 
    
    //逐次処理でデーター収集。速度より安定性重視。(-Sモード、-Eおよびオプション無しリトライ) 
    static void GetSequential()
    {
        //Console.WriteLine("==== SafetyMode: 逐次取得実行 ====");
        for (int i = 0; i < MaxMethod; i++)
        {
            var map = mappings[i];
            
            if (!expertMode && map.IsExtended)
                continue;

            if (string.IsNullOrWhiteSpace(sb_data[i]))
            {
                sb_data[i] = map.Method();
                Console.WriteLine(map.Comment);
            }
        }
    }

    // 起動直後に呼び出すウォームアップ処理
    /*
    static void WarmUp()
    {
        try
        {
            Task[] warmTasks = new Task[]
            {
            Task.Run(() =>
            {
                var wmi = new ManagementObjectSearcher("SELECT * FROM Win32_OperatingSystem");
                foreach (var _ in wmi.Get()) break;
            }),

            Task.Run(() =>
            {
                var _ = Registry.LocalMachine.OpenSubKey("SOFTWARE");
            }),

            Task.Run(() =>
            {
                using (EventLog log = new EventLog("System"))
                {
                    var _ = log.Entries.Count;
                }
            }),

            Task.Run(() =>
            {
                _ = Environment.MachineName;
                _ = DateTime.Now.ToString();
            })
            };

            Task.WaitAll(warmTasks);
        }
        catch
        {
            // ウォームアップは失敗しても問題なし
        }
    }




    static void WarmUp()
    {
        try
        {
            // 1. 軽い WMI クエリ（Win32_OperatingSystem）
            var wmi = new ManagementObjectSearcher("SELECT * FROM Win32_OperatingSystem");
            foreach (var _ in wmi.Get()) break;

            // 2. 軽いレジストリアクセス
            var _reg = Registry.LocalMachine.OpenSubKey("SOFTWARE");

            // 3. 軽いイベントログアクセス（System）
            using (EventLog log = new EventLog("System"))
            {
                var _ = log.Entries.Count;
            }

            // 4. 軽い .NET JIT ウォームアップ（簡単なメソッド呼び出し）
            _ = Environment.MachineName;
            _ = DateTime.Now.ToString();
        }
        catch
        {
            // ウォームアップは失敗しても無視してよい
        }
    }
    */



    /*
   --- 設計思想：人通信プロトコル仮説 ---

   この仮説は、人間の認知や対話における摩擦を、ネットワークプロトコルの概念で整形・観察する設計思想です。

   - 人間の認知を三層モデル（L1:入力 → L2:演算 → L3:出力）として捉える
   - 対話をOSI参照モデルに見立て、L4以降の語彙・前提整合性の違いが摩擦を生む
   - RIPのように、整合性の高い相手との通信が優先され、断絶が加速する構造を説明可能

   この仮説は、人対AIの鏡像実験にも応用可能です。
   ObservePCは、AIとの対話における前提整形テンプレートとしてPC環境を提示し、
   摩擦の構造を記録・整形することで、応答精度の向上とメトリックス悪化の回避を目指します。
   */

}
